package com.chy.blog_web.core.util;

import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.dysmsapi20170525.AsyncClient;
import com.aliyun.sdk.service.dysmsapi20170525.models.QuerySendDetailsRequest;
import com.aliyun.sdk.service.dysmsapi20170525.models.QuerySendDetailsResponse;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.google.gson.Gson;
import darabonba.core.client.ClientOverrideConfiguration;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/*
pom.xml
<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>aliyun-java-sdk-core</artifactId>
  <version>4.0.3</version>
</dependency>
*/

/**
 * 阿里云短信发送工具类
 */
public class SendSmsUtil {

    /**
     * accessKeyId
     */
    private static String ACCESSKEYID = "LTAI4GK4NxGz5j8TFdSWbG4y";

    /**
     * accessSecret
     */
    private static String ACCESSSECRET = "vJ0StqSDmkfQqCyqbDtBLxnm3c7rDS";

    private static String DOMAIN = "dysmsapi.aliyuncs.com";

    private static String VERSION = "2017-05-25";

    private static String ACTION = "SendSms";

    private static String REGIONID = "cn-hangzhou";

    /**
     * 短信签名名称（通用）
     */
    public static String SIGNNAME_CODE = "陈陈陈博客空间";

    /**
     * 节日通用模板-旧-----模板code（停用）
     * 模板：博客空间特殊日子提醒：${time} 是 ${timeContent}，距离该日子还有 ${dayNum} 天，要注意留意哦！
     * 变量属性：time-时间；timeContent-其他；dayNum-时间
     */
    public static String TEMPLATE_CODE_SMARK_DATE = "SMS_189025314";

    /**
     * 节日通用模板-新-----模板code
     * 模板：特殊日子提醒：${time} 是 ${holidays} ，距离该日子还有 ${dayNum} 天，要注意留意哦！
     * 变量属性：${time}-时间；${holidays}-节日名称；dayNum-天数
     */
    public static String TEMPLATE_CODE_GENERAL = "SMS_471410333";

    /**
     * 生日/纪念日-当天-----模板code
     * 模板：节日提醒：今天是 ${dayName} ，好好庆祝一下吧！
     * 变量属性：${dayName}-节日名称
     */
    public static String TEMPLATE_CODE_MARK_DATE_CURRENT = "SMS_471330305";

    /**
     * 普通节日-当天-----模板code
     * 模板：节日提醒：今天是 ${dayName} ，好好放松一下吧！
     * 变量属性：${dayName}-节日名称
     */
    public static String TEMPLATE_CODE_COMMON_DATE_CURRENT = "SMS_471430340";

    /**
     * 备忘事项-当天-----模板code
     * 模板：备忘提醒：您今天有一件待办事项 [${title}] 需要完成 ，别忘记了哦！
     * 变量属性：${title}-待办事项
     */
    public static String TEMPLATE_CODE_BACKLOG_DATE_CURRENT = "SMS_471455321";

    /**
     * 看书动态模板code
     * 模板：${userName} 在博客空间发起了一条看书清单，书名：${bookName}，目录：${directoryName}，章节：${listName}，开始时间：${startTime}，结束时间：${endTime}
     * 变量属性：userName-其他；bookName-其他；directoryName-其他；listName-其他；startTime-时间；endTime-时间
     */
    public static String TEMPLATE_CODE_SEE_BOOK = "SMS_188636674";

    /**
     * 恋爱清单模板code
     * 模板：${uaerName} 在博客空间发起了一条恋爱清单，内容：${content}
     * 变量属性：uaerName-其他；content-其他
     */
    public static String TEMPLATE_CODE_LOVE_LIST = "SMS_187932586";

    /**
     * 错误状态码
     */
    public static Map<String, String> errorMap = new HashMap<>();

    static {
        errorMap.put("isv.SMS_SIGNATURE_SCENE_ILLEGAL", "签名和模板类型不一致");
        errorMap.put("isv.EXTEND_CODE_ERROR", "扩展码使用错误，相同的扩展码不可用于多个签名");
        errorMap.put("isv.DOMESTIC_NUMBER_NOT_SUPPORTED", "国际/港澳台消息模板不支持发送境内号码");
        errorMap.put("isv.DENY_IP_RANGE", "原IP地址所在的地区被禁用");
        errorMap.put("isv.DAY_LIMIT_CONTROL", "触发日发送限额");
        errorMap.put("isv.MONTH_LIMIT_CONTROL", "触发月发送限额");
        errorMap.put("isv.SMS_CONTENT_ILLEGAL", "短信内容包含禁止发送内容");
        errorMap.put("isv.SMS_SIGN_ILLEGAL", "签名禁止使用");
        errorMap.put("isp.RAM_PERMISSION_DENY", "RAM权限不足");
        errorMap.put("isv.OUT_OF_SERVICE", "业务停机");
        errorMap.put("isv.PRODUCT_UN_SUBSCRIPT", "未开通云通信产品的阿里云客户");
        errorMap.put("isv.PRODUCT_UNSUBSCRIBE", "产品未开通");
        errorMap.put("isv.ACCOUNT_NOT_EXISTS", "账户不存在");
        errorMap.put("isv.ACCOUNT_ABNORMAL", "账户异常");
        errorMap.put("isv.SMS_TEMPLATE_ILLEGAL", "请检查模板内容与模板参数是否匹配");
        errorMap.put("isv.SMS_SIGNATURE_ILLEGAL", "该账号下找不到对应签名");
        errorMap.put("isv.INVALID_PARAMETERS", "参数格式不正确");
        errorMap.put("isp.SYSTEM_ERROR", "系统出现错误，请重新调用");
        errorMap.put("isv.MOBILE_NUMBER_ILLEGAL", "手机号码格式错误");
        errorMap.put("isv.MOBILE_COUNT_OVER_LIMIT", "调用接口时参数PhoneNumbers中指定的手机号码数量超出限制。");
        errorMap.put("isv.TEMPLATE_MISSING_PARAMETERS", "模板变量中存在未赋值变量");
        errorMap.put("isv.BUSINESS_LIMIT_CONTROL", "触发云通信流控限制");
        errorMap.put("isv.INVALID_JSON_PARAM", "参数格式错误，请修改为字符串值");
        errorMap.put("isv.BLACK_KEY_CONTROL_LIMIT", "黑名单管控");
        errorMap.put("isv.PARAM_LENGTH_LIMIT", "参数超过长度限制");
        errorMap.put("isv.PARAM_NOT_SUPPORT_URL", "变量不支持传入URL");
        errorMap.put("isv.AMOUNT_NOT_ENOUGH", "账户余额不足");
        errorMap.put("isv.TEMPLATE_PARAMS_ILLEGAL", "传入的变量内容和实际申请模板时变量所选择的属性类型不配");
        errorMap.put("SignatureDoesNotMatch", "客户端生成的签名与服务端不匹配");
        errorMap.put("InvalidTimeStamp.Expired", "时间戳或日期已过期");
        errorMap.put("SignatureNonceUsed", "签名随机数已被使用");
        errorMap.put("InvalidVersion", "API版本号错误");
        errorMap.put("InvalidAction.NotFound", "未找到指定的API，请检查您的URL和方法");
        errorMap.put("isv.SIGN_COUNT_OVER_LIMIT", "超过单自然日签名申请数量上限");
        errorMap.put("isv.TEMPLATE_COUNT_OVER_LIMIT", "超过单自然日模板申请数量上限");
        errorMap.put("isv.SIGN_NAME_ILLEGAL", "签名名称不符合规范");
        errorMap.put("isv.SIGN_FILE_LIMIT", "签名认证材料附件大小超过限制");
        errorMap.put("isv.SIGN_OVER_LIMIT", "签名字符数量超过限制");
        errorMap.put("isv.TEMPLATE_OVER_LIMIT", "模板字符数量超过限制");
        errorMap.put("SIGNATURE_BLACKLIST", "签名内容涉及违规信息");
        errorMap.put("isv.SHORTURL_OVER_LIMIT", "超过单自然日短链申请数量上限");
        errorMap.put("isv.NO_AVAILABLE_SHORT_URL", "该账号无有效短链");
        errorMap.put("isv.SHORTURL_NAME_ILLEGAL", "短链名不能超过13字符");
        errorMap.put("isv.SOURCEURL_OVER_LIMIT", "原始链接字符数量超过限制");
        errorMap.put("isv.SHORTURL_TIME_ILLEGAL", "短链有效期期限超过限制");
        errorMap.put("isv.PHONENUMBERS_OVER_LIMIT", "上传手机号个数超过上限");
        errorMap.put("isv.SHORTURL_STILL_AVAILABLE", "原始链接生成的短链仍在有效期内");
        errorMap.put("isv.ERROR_EMPTY_FILE", "签名文件为空");
        errorMap.put("isp.GATEWAY_ERROR", "调用发送应用模块失败");
        errorMap.put("isv.ERROR_SIGN_NOT_DELETE", "审核中的签名，暂时无法删除");
        errorMap.put("isv.ERROR_SIGN_NOT_MODIFY", "已通过的签名不支持修改");
        errorMap.put("isv.ERROR_TEMPLATE_NOT_DELETE", "审核中的模板，暂时无法删除");
        errorMap.put("isv.ERROR_TEMPLATE_NOT_MODIFY", "已通过的模板不支持修改");
        errorMap.put("isv.SMS_OVER_LIMIT", "单日最多申请模板或签名100条");
        errorMap.put("isv.CUSTOMER_REFUSED", "用户已退订推广短信");
        errorMap.put("isv.SMS_TEST_SIGN_TEMPLATE_LIMIT", "测试模板和签名限制");
        errorMap.put("isv.SHORTURL_DOMAIN_EMPTY", "短链创建失败");
        errorMap.put("template_parameter_count_illegal", "验证码模板仅支持一个验证码作为变量");
        errorMap.put("isv.SMS_TEST_TEMPLATE_PARAMS_ILLEGAL", "测试专用模板中的变量仅支持4~6位纯数字");
        errorMap.put("isv.SMS_TEST_NUMBER_LIMIT", "只能向已绑定的手机号发送");
        errorMap.put("isv.SMS_SIGN_EMOJI_ILLEGAL", "签名不能包含emoji表情");
        errorMap.put("isv.SECURITY_FROZEN_ACCOUNT", "因该账号长时间未使用，出于对您的账号安全考虑，已限制您账号的短信发送");
        errorMap.put("IS_CLOSE", "短信下发时通道被关停");
        errorMap.put("PARAMS_ILLEGAL", "参数错误");
        errorMap.put("MOBILE_NOT_ON_SERVICE", "停机、空号、暂停服务、关机、不在服务区");
        errorMap.put("MOBILE_SEND_LIMIT", "单个号码日、月发送上限，流控超限，频繁发送超限");
        errorMap.put("MOBILE_ACCOUNT_ABNORMAL", "用户账户异常、携号转网、欠费等");
        errorMap.put("MOBILE_IN_BLACK", "手机号在黑名单");
        errorMap.put("MOBLLE_TERMINAL_ERROR", "手机终端问题、内存满、SIM卡满、非法设备等");
        errorMap.put("CONTENT_KEYWORD", "内容关键字拦截");
        errorMap.put("INVALID_NUMBER", "号码状态异常");
        errorMap.put("CONTENT_ERROR", "内容无退订");
        errorMap.put("REQUEST_SUCCESS", "请求成功（平台接到请求，但未接收到运营商侧的回执状态）");
        errorMap.put("SP_NOT_BY_INTER_SMS", "未开通国际短信");
        errorMap.put("SP_UNKNOWN_ERROR", "运营商未知错误");
        errorMap.put("USER_REJECT", "接收用户已退订此业务或产品未开通");
        errorMap.put("NO_ROUTE", "当前短信内容无可用通道发送");
        errorMap.put("UNSUPPORTED_CONTENT", "不支持的短信内容");
        errorMap.put("isv.SMS_CONTENT_MISMATCH_TEMPLATE_TYPE", "短信内容和模板属性不匹配");
        errorMap.put("isv.ONE_CODE_MULTIPLE_SIGN", "一码多签");
        errorMap.put("isv.CODE_EXCEED_LIMIT", "自拓扩展码个数已超过上限");
        errorMap.put("isv.CODE_ERROR", "传入扩展码不可用");
    }

    /**
     * 发送短信
     * @param phoneNumbers 支持对多个手机号码发送短信，手机号码之间以英文逗号（,）分隔。上限为1000个手机号码。批量调用相对于单条调用及时性稍有延迟。
     * @param signName 短信签名名称。请在控制台签名管理页面签名名称一列查看。  必须是已添加、并通过审核的短信签名。
     * @param templateCode 短信模板ID。请在控制台模板管理页面模板CODE一列查看。  必须是已添加、并通过审核的短信签名；且发送国际/港澳台消息时，请使用国际/港澳台短信模版。
     * @param templateParam 短信模板变量对应的实际值，JSON格式。  如果JSON中需要带换行符，请参照标准的JSON协议处理。
     */
    public static String SendSms(String phoneNumbers, String signName, String templateCode, String templateParam) {
        try {
            DefaultProfile profile = DefaultProfile.getProfile(REGIONID, ACCESSKEYID, ACCESSSECRET);
            IAcsClient client = new DefaultAcsClient(profile);

            CommonRequest request = new CommonRequest();
            request.setMethod(MethodType.POST);
            request.setDomain(DOMAIN);
            request.setVersion(VERSION);
            request.setAction(ACTION);
            request.putQueryParameter("RegionId", REGIONID);
            request.putQueryParameter("PhoneNumbers", phoneNumbers);
            if(StringUtil.isNull(signName)){
                request.putQueryParameter("SignName", SIGNNAME_CODE);
            }else{
                request.putQueryParameter("SignName", signName);
            }
            request.putQueryParameter("TemplateCode", templateCode);
            request.putQueryParameter("TemplateParam", templateParam);

            CommonResponse response = client.getCommonResponse(request);
            return response.getData();
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询运营商发送短信状态
     * @param phone 手机号
     * @param bizId 回执ID
     * @param sendDate 发送日期，格式：yyyyMMdd
     * @return
     */
    public static String getMsgState(String phone, String bizId, String sendDate) {
        try{
            // Configure Credentials authentication information, including ak, secret, token
            StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                    .accessKeyId(ACCESSKEYID)
                    .accessKeySecret(ACCESSSECRET)
                    .build());

            // Configure the Client
            AsyncClient client = AsyncClient.builder()
                    .region(REGIONID) // Region ID
                    //.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
                    .credentialsProvider(provider)
                    //.serviceConfiguration(Configuration.create()) // Service-level configuration
                    // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
                    .overrideConfiguration(
                            ClientOverrideConfiguration.create()
                                    // Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
                                    .setEndpointOverride(DOMAIN)
                            //.setConnectTimeout(Duration.ofSeconds(30))
                    )
                    .build();

            // Parameter settings for API request
            QuerySendDetailsRequest querySendDetailsRequest = QuerySendDetailsRequest.builder()
                    .phoneNumber(phone)
                    .bizId(bizId)
                    .sendDate(sendDate)
                    .pageSize(50L) //每页记录数
                    .currentPage(1L) //当前页数
                    // Request-level configuration rewrite, can set Http request parameters, etc.
                    // .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
                    .build();;

            // Asynchronously get the return value of the API request
            CompletableFuture<QuerySendDetailsResponse> response = client.querySendDetails(querySendDetailsRequest);
            // Synchronously get the return value of the API request
            QuerySendDetailsResponse resp = response.get();
            String result = new Gson().toJson(resp);
            System.out.println(result);

            // Finally, close the client
            client.close();
            return result;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
